﻿using System;
using System.CodeDom;
using System.Linq;
using Roslyn.Compilers.CSharp;

namespace InterfaceWeaver.CustomTool
{
    public sealed class PropertyDeclaration
    {
        private readonly PropertyDeclarationSyntax @property = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyDeclaration" /> class.
        /// </summary>
        /// <param name="property">The property.</param>
        public PropertyDeclaration(PropertyDeclarationSyntax @property)
        {
            this.@property = @property;
        }


        /// <summary>
        /// Creates the code namespace.
        /// </summary>
        /// <returns></returns>
        public CodeMemberPropertyContext CreateCodePropertyImplementation()
        {
            var codeTypeReference = this.@property.Type.ToTypeReference();
            var propertyNameToWeave = this.@property.Identifier.ValueText;
            var privateFieldName = propertyNameToWeave.ToCamelCase();
            CodeMemberPropertyContext codeMemberPropertyContext;

            // Property creation
            var accessorToWeaveGet = this.@property.FindGetter();
            var accessorToWeaveSet = this.@property.FindSetter();
            var weavedProperty = new CodeMemberProperty
            {
                Name = propertyNameToWeave,
                Type = codeTypeReference,
                HasGet = accessorToWeaveGet != null,
                HasSet = accessorToWeaveSet != null,
                Attributes = MemberAttributes.Public | MemberAttributes.Final
            };

            // Backing field creation
            var weavedField = new CodeMemberField
            {
                Name = privateFieldName,
                Type = codeTypeReference,
                Attributes = MemberAttributes.Private
            };

            var codeFieldReference = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), weavedField.Name);

            // Getter implementation
            if (weavedProperty.HasGet)
            {
                weavedProperty.GetStatements.Add(new CodeMethodReturnStatement(codeFieldReference));
            }

            // Setter implementation
            if (weavedProperty.HasSet)
            {
                // hooks creation
                //var valueChangingHookName = string.Format("OnProperty{0}ChangingCallBack", weavedProperty.Name);
                //var valueChangedHookName = string.Format("OnProperty{0}ChangedCallBack", weavedProperty.Name);
                //var valueChangingHook = new CodeSnippetTypeMember(string.Format("\t\tpartial void {0}(string propertyName);", valueChangingHookName));
                //var valueChangedHook = new CodeSnippetTypeMember(string.Format("\t\tpartial void {0}(string propertyName);", valueChangedHookName));

                // Setter implementation
                var fieldAssignmentStatment = new CodeAssignStatement(codeFieldReference,new CodePropertySetValueReferenceExpression());
                var setStatmentCoreStream = weavedProperty.SetStatements;
                //setStatmentCoreStream.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), valueChangingHookName, new CodePrimitiveExpression(weavedProperty.Name)));
                //setStatmentCoreStream.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "OnPropertyChangingCallBack", new CodePrimitiveExpression(weavedProperty.Name)));
                setStatmentCoreStream.Add(fieldAssignmentStatment);
                //setStatmentCoreStream.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), valueChangedHookName, new CodePrimitiveExpression(weavedProperty.Name)));
                //setStatmentCoreStream.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "OnPropertyChangedCallBack", new CodePrimitiveExpression(weavedProperty.Name)));
                
                // code context creation
                codeMemberPropertyContext = new CodeMemberPropertyContext(weavedProperty, weavedField, setStatmentCoreStream, fieldAssignmentStatment);//, valueChangingHook, valueChangedHook);
            }
            else
            {
                codeMemberPropertyContext = new CodeMemberPropertyContext(weavedProperty, weavedField, null, null);
            }

            // Property comments
            var comments = this.@property.DescendantTrivia().FirstOrDefault(tr => tr.Kind == SyntaxKind.DocumentationCommentTrivia);
            var commentText = comments.ToFullString();
            if (!string.IsNullOrWhiteSpace(commentText))
            {
                var commentTextLines =
                    commentText
                    .Split(new[] { "\r\n", "\n" }, StringSplitOptions.None)
                    .Select(s => s.TrimStart('/', ' '))
                    .Where(s => !string.IsNullOrWhiteSpace(s)).ToList();
                weavedProperty.Comments.AddRange(commentTextLines.Select(l => new CodeCommentStatement(l, true)).ToArray());
            }

            return codeMemberPropertyContext;
        }
    }
}
